#include <peekpoke.h>
#include <string.h>
#include <cbm.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

#include "Types.h"
#include "Output.h"

byte_t _pos_x = 0;
byte_t _pos_y = 0;
byte_t _fore_color = COLOR_BLACK;
bool _reverse_on = false;

bool isDoubleBuffered = false;
static unsigned char d018;
static unsigned char dd00;

void __fastcall__ writenumberxy(byte_t xx, byte_t y, long x)
{
	long a;
	if(x > 9)
	{
		a = x / 10;
		x -= 10 * a;
		writenumberxy(xx - 1, y, a);
	}
	plotasciixy(xx, y, '0' + x);
}

void __fastcall__ beginDoubleBuffer(void)
{
	static bool double_buffer_initialized = false;

	if(!double_buffer_initialized)
	{
		--CIA1.cra;						// Turn off interrupt timer
		POKE(0x01, PEEK(0x01) & ~0x04);	// Switch in character ROM

		// Copy the lower-/upper-case font into VIC-II RAM bank 3.
		memcpy((void*)0xF800, (void*)0x3000, 0x0800);

		POKE(0x01, PEEK(0x01) | 0x04);	// Switch out character ROM
		++CIA1.cra;						// Turn on interrupt timer

		double_buffer_initialized = true;

		// Save the original VIC-II banking values.
		d018 = PEEK(0xD018);
		dd00 = PEEK(0xDD00);
	}

	memcpy(0xF000, CHARMAP, 1000);
	memcpy(0xF400, COLORMAP, 1000);

	POKE(0xDD00, (PEEK(0xDD00) & 0xFC) | 0x00);	// switch to VIC-II bank 3
	POKE(0xD018, 0xC0 | 0x0E);					// show the saved screen

	isDoubleBuffered = true;
}

void __fastcall__ endDoubleBuffer(void)
{
	POKE(0xDD00, dd00);					// switch back to VIC-II bank 0
	POKE(0xD018, d018);					// show the standard screen area

	isDoubleBuffered = false;
}

byte_t __fastcall__ getScreenCode(char source, bool reverse)
{
	byte_t target;
		
	if(source == 0) return 0;
	else if(source >= 1 && source <= 31) target = 32;
	else if(source >= 32 && source <= 63) target = source;
	else if(source >= 64 && source <= 95) target = source - 64;
	else if(source >= 97 && source <= 127) target = source - 32;
	else if(source >= 128 && source <= 159) target = 32;
	else if(source >= 160 && source <= 191) target = source - 64;
	else if(source >= 192 && source <= 254) target = source - 128;
	else if(source == 255) target = 94;
		
	if(reverse)
		target += 128;

	return target;
}

void __fastcall__ getScreenCodes(char *target, char *source, byte_t length, bool reverse)
{
	byte_t i;

	memset(target, ' ', length);

	for(i = 0; i < length; ++i)
	{
		//if(source[i] == 0) break;
		target[i] = getScreenCode(source[i], reverse);
	}
}

//void movexy(byte_t x, byte_t y)
//{
//	_pos_x = x;
//	_pos_y = y;
//}

void plotcode(byte_t screenCode)
{
	static unsigned offset;

	offset = _pos_x + _pos_y * 40u;

	POKE(COLORMAP + offset, (char)_fore_color);
	POKE(CHARMAP + offset,  (char)screenCode);
}

void plotcodexy(byte_t x, byte_t y, byte_t screenCode)
{
	_pos_x = x;
	_pos_y = y;
	plotcode(screenCode);
}

byte_t __fastcall__ codeat(byte_t x, byte_t y)
{
	return *((byte_t*)(CHARMAP + (x + y * 40u)));
}

void plotascii(char ascii)
{
	plotcode(getScreenCode(ascii, _reverse_on));
}

void plotasciixy(byte_t x, byte_t y, char ascii)
{
	plotcodexy(x, y, getScreenCode(ascii, _reverse_on));
}

void writestringxy(byte_t x, byte_t y, char *string)
{
	static unsigned offset;
	static byte_t len;
	static char temp[1001];

	len = strlen(string);
	if(len == 0) return;

	memset(temp, 0, 1000);

	_pos_x = x;
	_pos_y = y;

	offset = _pos_x + _pos_y * 40u;

	if(len > (40*25 - offset)) len = (40*25 - offset);

	getScreenCodes(temp, string, len, _reverse_on);
	
	memcpy((void*)(CHARMAP + offset), temp, len);
	memset((void*)(COLORMAP + offset), _fore_color, len);
}

bool clearxy(byte_t x, byte_t y, uint_t len)
{
	static uint_t offset;
	offset = x + y * 40u;
	if(offset + len > 40 * 25) return false;
	memset((void*)(CHARMAP + offset), ' ', len);
	return true;
}

void moveline(char* sourcemap, byte_t x, byte_t y, byte_t w, 
			  char* targetmap, byte_t tx, byte_t ty)
{
	static uint_t sourceOffset = 0; 
	static uint_t targetOffset = 0; 
	static uint_t toMove = 0;

	if(y > 24 || ty > 24) 
	{
		return;
	}

	sourceOffset = x + y * 40u;
	targetOffset = tx + ty * 40u;
	toMove = w;

	if(targetOffset + w > 40u * 25u) toMove = 40u * 25u - targetOffset;
	if(sourceOffset + w > 40u * 25u) toMove = 40u * 25u - sourceOffset;

	memcpy((void*)(targetmap + targetOffset), (void*)(sourcemap + sourceOffset), toMove);

	if(sourcemap != (void*)COLORMAP)
	{
		memset((void*)(sourcemap + sourceOffset), 0x20, toMove);
	}
}

void moveregion(byte_t x, byte_t y, byte_t w, byte_t h, byte_t tx, byte_t ty)
{
	static byte_t i;
	static char *charbuffer = 0xC000;
	static char *colorbuffer = 0xC400;

	beginDoubleBuffer();

	for(i = 0; i < h; ++i)
	{
		moveline((char*)CHARMAP, x, y + i, w, charbuffer, tx, ty + i);
		moveline((char*)COLORMAP, x, y + i, w, colorbuffer, tx, ty + i);
	}

	for(i = 0; i < h; ++i)
	{
		moveline(charbuffer, tx, ty + i, w, (char*)CHARMAP, tx, ty + i);
		moveline(colorbuffer, tx, ty + i, w, (char*)COLORMAP, tx, ty + i);
	}

	endDoubleBuffer();
}

void clearscreen()
{
	memset((void*)CHARMAP, 0x20, 1000);
}